/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.aaron.platform.service;

import com.aaron.platform.common.BeanUtils;
import com.aaron.platform.dao.IRolePriDao;
import com.aaron.platform.dao.IXmlParser;
import com.aaron.platform.dao.IRoleDao;
import com.aaron.platform.exceptions.RoleAlreadyExistException;
import com.aaron.platform.exceptions.RoleNotExistException;
import com.aaron.platform.exceptions.RoleUserExistException;
import com.aaron.platform.exceptions.XmlParseException;
import com.aaron.platform.exceptions.XmlPathException;
import com.aaron.platform.entity.ItscPriExt;
import com.aaron.platform.entity.ItscPriInfo;
import com.aaron.platform.entity.ItscRoleInfo;
import com.aaron.platform.entity.ItscRolePrivileges;

import com.aaron.platform.entity.ItscUserInfo;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.apache.myfaces.custom.tree2.TreeModel;
import org.springframework.dao.DataAccessException;

/**
 * 权限管理接口实现
 * @author chenzhuohua
 */
public class AuthoritySeviceImp implements IAuthorityService{

    private static Logger logger = Logger.getLogger(AuthoritySeviceImp.class);

    /** 权限配置文件的目录 */
    private final static String CONFIG_PATH = "WEB-INF";

    /** 权限配置文件名 */
    private final static String PRIVILEGE_FILE_NAME = "privileges.xml";

    /** default privilege ext value 0 */
    private final static Integer DEFAULT_EXTPRI_VALUE = 0;

    /** 保存角色对应的具体权限值<roleId, privilegeInfo> */
    private Map<Integer, Map<String, Integer>> rolePrivileges =
            new HashMap<Integer, Map<String, Integer>>();

    /** 权限名与权限信息的对应关系 */
    private Map<String, ItscPriInfo> privilegInfos =
            new HashMap<String, ItscPriInfo>();

    /** 用户操作接口 */
    private IUserService userService;

    /** 角色DAO接口 */
    private IRoleDao roleDao;

    /** 角色权限DAO接口 */
    private IRolePriDao rolePriDao;

    /** xml解析操作接口 */
    private IXmlParser xmlParser;

    public void setXmlParser(IXmlParser xmlParser) {
        this.xmlParser = xmlParser;
    }

    public void setUserService(IUserService userService) {
        this.userService = userService;
    }

    public void setRoleDao(IRoleDao roleDao) {
        this.roleDao = roleDao;
    }

    public void setRolePriDao(IRolePriDao rolePriDao) {
        this.rolePriDao = rolePriDao;
    } 

    /**
     * 从配置文件获取可配置权限，并把所有用户的权限信息写入内存
     * @throws XmlPathException
     * @throws XmlParseException
     */
    public void init() throws XmlPathException, XmlParseException {

        // 获取配置文件的路径
        String classPath = getClass().getProtectionDomain().getCodeSource()
                 .getLocation().getPath();
        String xmlPath = classPath.substring(DEFAULT_EXTPRI_VALUE,
                classPath.indexOf(CONFIG_PATH) + 8);

        String priXmlPath = xmlPath + File.separator + PRIVILEGE_FILE_NAME;

        // 解析配置文件，得到权限信息
        File priXml = new File(priXmlPath);
        List<ItscPriInfo> itscPriInfos = xmlParser.parsePreInfoXml(priXml);

        // 保存权限信息到内存
        for (ItscPriInfo priInfo : itscPriInfos) {
            privilegInfos.put(priInfo.getPrivilegeName(), priInfo);
        }

        // 获取所有角色的权限信息，保存到内存
        List<ItscRoleInfo> roleInfos = findAllRoles();
        for (ItscRoleInfo roleInfo : roleInfos) {
            List<ItscRolePrivileges> privilegeInfos = rolePriDao.findByRoleId(roleInfo.getRoleId());
            updateMemRolePrivileges(roleInfo.getRoleId(), privilegeInfos);
        }
    }

    public ItscRoleInfo findRoleById(Integer roleId) throws RoleNotExistException{
        ItscRoleInfo roleInfo = roleDao.findById(roleId);

        // 不存在抛出异常
        if (roleInfo == null){
           throw new RoleNotExistException();
        }
        return roleInfo;
    }

    public ItscRoleInfo findRoleByName(String roleName) throws RoleNotExistException {
        // throw a exception if the role does not exist
        ItscRoleInfo roleInfo = roleDao.findRoleByName(roleName);
        if (roleInfo == null){
            throw new RoleNotExistException();
        }
        return roleInfo;
    }

    public List<ItscRoleInfo> findAllRoles() {
        return roleDao.FindAll();
    }

    public void addRole(ItscRoleInfo roleInfo) throws RoleAlreadyExistException{
        // role name should be unique
        ItscRoleInfo role = roleDao.findRoleByName(roleInfo.getRoleName());
        if (role != null){
            throw new RoleAlreadyExistException(roleInfo.getRoleName());
        }
        // add a role
        roleDao.save(roleInfo);
    }

    public void updateRole(ItscRoleInfo roleInfo) throws RoleAlreadyExistException, RoleNotExistException {
        // could not change a name which already exists
        ItscRoleInfo role = roleDao.findRoleByName(roleInfo.getRoleName());
        if (!role.equals(roleInfo)){
            throw new RoleAlreadyExistException(roleInfo.getRoleName());
        }
        try{
            roleDao.update(roleInfo);
        }
        catch(DataAccessException dae){
            // tranlate the exception for the role has already been removed
            role = roleDao.findById(roleInfo.getRoleId());
            if (role == null){
                throw new RoleNotExistException();
            }
            // throw the exception for other conditions
            throw dae;
        }
    }

    public void removeRole(Integer roleId) throws RoleNotExistException, RoleUserExistException {
        // if the role is referenced by any user, you can't delete it
        // if referenced, throw RoleUserExistException
        List<ItscUserInfo> userInfos = userService.findUsersByRoleId(roleId);
        if (userInfos.size() > 0) {
            throw new RoleUserExistException();
        }

        // delete it when the role is not referenced
        try {
            // delete all the privileges
            updateRolePrivilege(roleId, null);
            roleDao.removeById(roleId);
        }
        catch (DataAccessException dae) {
            // role does not exist
            ItscRoleInfo roleInfo = roleDao.findById(roleId);
            if (roleInfo == null) {
                throw new RoleNotExistException();
            }
            // throw it when other conditions
            throw dae;
        }
    }

    public void updateRolePrivilege(Integer roleId, List<ItscPriInfo> priInfos) throws RoleNotExistException {

        // 清空原有的权限配置
        List<ItscRolePrivileges> privilegeses = rolePriDao.findByRoleId(roleId);
        if (privilegeses != null) {
            rolePriDao.removeAll(privilegeses);
            updateMemRolePrivileges(roleId, null);
        } else {
            privilegeses = new ArrayList<ItscRolePrivileges>();
        }

        // 构造角色权限值
        for (ItscPriInfo priInfo : priInfos) {
            ItscRolePrivileges rolePris = new ItscRolePrivileges();
            rolePris.setPrivilegeName(priInfo.getPrivilegeName());
            rolePris.setRolePriId(roleId);
            rolePris.setPrevilegeValue(caculatePriValue(priInfo.getItscPriExtList()));

            // 保存到列表
            privilegeses.add(rolePris);
        }

        // 保存到数据库
        try {
            rolePriDao.saveOrUpdateAll(privilegeses);
            updateMemRolePrivileges(roleId, privilegeses);
        }
        catch (DataAccessException dae) {

            // 角色被删除
            ItscRoleInfo roleInfo = roleDao.findById(roleId);
            if (roleInfo == null) {
                logger.error("role (ID:" + roleId + ") does not exist.");
                throw new RoleNotExistException();
            }
            throw dae;
        }
    }

    public List<ItscPriInfo> findRolePrivileges(Integer roleId) {
        Map<String, Integer> rolePriInfos = rolePrivileges.get(roleId);
        if (rolePriInfos == null) {
            return null;
        }
        List<ItscPriInfo> priInfos = new ArrayList<ItscPriInfo>();
        Set<Map.Entry<String, Integer>> rolePriMap = rolePriInfos.entrySet();
        for (Map.Entry<String, Integer> me : rolePriMap) {
            Integer priValue = me.getValue();
            ItscPriInfo priInfo = this.privilegInfos.get(me.getKey());
            if (priInfo == null) {
                return null;
            }
            // deep copy
            ItscPriInfo returnPriInfo = (ItscPriInfo) BeanUtils.objectClone(priInfo);
            for (ItscPriExt priExt : returnPriInfo.getItscPriExtList()) {
                // 0 means the ext privilege is enable
                if (priValue % priExt.getOperValue() == 0) {
                    priExt.setEnabled(true);
                }
            }
            priInfos.add(returnPriInfo);
        }
        return priInfos;
    }

    public List<ItscPriInfo> findUserPrivileges(ItscUserInfo userInfo) {
        return findRolePrivileges(userInfo.getRoleInfo().getRoleId());
    }

    public boolean hasPrivilege(Integer roleId, String privilegeName) {
        Map<String, Integer> rolePriInfos = rolePrivileges.get(roleId);
        if (rolePriInfos == null) {
            return false;
        }
        if (rolePriInfos.get(privilegeName) != null) {
            return true;
        }
        return false;
    }

    public boolean hasExtPrivilege(Integer roleId, String privilegeName, String extName) {
        Map<String, Integer> rolePriInfos = rolePrivileges.get(roleId);
        if (rolePriInfos == null) {
            return false;
        }
        Integer privilegeValue = rolePriInfos.get(privilegeName);

        if (privilegeValue == null) {
            return false;
        }
        Integer extValue = getExtPrivateValue(privilegeName, extName);
        if (extValue == DEFAULT_EXTPRI_VALUE) {
            return false;
        }

        if (privilegeValue % extValue == DEFAULT_EXTPRI_VALUE) {
            return true;
        }

        return false;
    }

    public boolean hasPrivilege(ItscUserInfo userInfo, String privilegeName) {
        return hasPrivilege(userInfo.getRoleInfo().getRoleId(), privilegeName);
    }
    

    public boolean hasExtPrivilege(ItscUserInfo userInfo, String privilegeName, String extName) {
        return hasExtPrivilege(userInfo.getRoleInfo().getRoleId(), privilegeName, extName);
    }

    /**
     * 根据扩展属性计算权限值
     * @param priExts 扩展属性
     * @return 权限值
     */
    private Integer caculatePriValue(List<ItscPriExt> priExts) {
        Integer total = 1;
        for (ItscPriExt ext : priExts) {
            if (ext.isEnabled()) {
                total = total * ext.getOperValue();
            }
        }
        return total;
    }


    /**
     * 
     * @param privilegeName
     * @param extName
     * @return
     */
    private Integer getExtPrivateValue(String privilegeName, String extName) {
        ItscPriInfo priInfo = privilegInfos.get(privilegeName);
        for (ItscPriExt priExt : priInfo.getItscPriExtList()) {
            if (extName.equals(priExt.getOperName())) {
                return priExt.getOperValue();
            }
        }
        return DEFAULT_EXTPRI_VALUE;
    }

    /**
     * update a role's privilege information in the memory
     * @param roleId role Id
     * @param privilegeses privilege information
     */
    private synchronized void updateMemRolePrivileges(Integer roleId, List<ItscRolePrivileges> privilegeses) {
        // delete it if privilege is null
        if (privilegeses == null) {
            this.rolePrivileges.remove(roleId);
        }

        // update the privilege
        Map<String, Integer> roleExtInfo = new HashMap<String, Integer>();
        for (ItscRolePrivileges rolePrivilege : privilegeses) {
                roleExtInfo.put(rolePrivilege.getPrivilegeName(), rolePrivilege.getPrivilegeValue());
        }
        rolePrivileges.put(roleId, roleExtInfo);
    }
}
